package model;

import algorithme.AlgorithmeBellmanFord;
import algorithme.AlgorithmePlusCourtChemin;
import test.Application;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: Thomas
 * Date: 04/02/14
 * Time: 10:26
 * Classe permettant la gestion du graph
 */
public class Graph {
    //Map contenant la liste des Arc triés par ligne de transport
    private HashMap<String, ArrayList<Arc>> arcs = new HashMap<String, ArrayList<Arc>>();
    //Map contenant la liste des noeuds triés par id
    private HashMap<Integer, Noeud> noeuds = new HashMap<Integer, Noeud>();
    //Noeud de source pour la recherche du plus court chermin
    private Noeud source;
    //Noeud de destination pour la recherche du plus court chermin
    private Noeud destination;
    //Algorithme choisi pour la résolution du plus court chemin source -> destination
    private AlgorithmePlusCourtChemin currentAlgorithme = new AlgorithmeBellmanFord();

    public Graph() { }

    public Graph(HashMap<String, ArrayList<Arc>> arcs, HashMap<Integer, Noeud> noeuds) {
        this.arcs = arcs;
        this.noeuds = noeuds;
    }

    /**
     * Affiche toutes les stations d'une ligne d'un terminus aux autres
     * @param ligne le numéro de la ligne choisie
     */
    public void afficheLigne(String ligne) {
        System.out.println(ligne);
        //Récupération d'un terminus
        Noeud start = this.getTerminus(ligne);
        System.out.println("Start: "+start.getName());

        //Tableau permettant le parcours de la ligne sans repasser par le même point
        ArrayList<Noeud> previous = new ArrayList<Noeud>();
        previous.add(start);
        //parcours de la ligne en recursif
        System.out.println(this.parcours(start, ligne, previous));
    }

    /**
     * Méthode permettant le parcours en récursion d'une ligne à partir de noeud
     * @param noeud Noeud de départ
     * @param ligne Numéro de la ligne choisie
     * @param previous liste des Noeud déjà visité
     */
    public String parcours(Noeud noeud, String ligne, ArrayList<Noeud> previous) {
        String ret = " -> " + noeud.getName();
        int i = 0;
        for (Arc arc : noeud.getNext()) {
            //Si l'arc est sur la ligne recherché et que sa destination n'a pas déjà été visitée
            if (arc.getLigne().equals(ligne) && !previous.contains(arc.getDestination())) {
                previous.add(noeud);
                //récusion
                ret += parcours(arc.getDestination(), ligne, previous);
                if (i > 0) {
                    ret += "\n";
                }
                i++;
            }
        }
        return ret;
    }

    /**
     * Méthode permettant de récupérer un terminus d'une ligne
     * @param ligne Le numéro de la ligne choisie
     * @return Un Noeud terminus
     */
    public Noeud getTerminus(String ligne) {
        ArrayList<Arc> arcs1 = this.getArcs().get(ligne);
        Noeud noeud = arcs1.get(0).getDestination();
        ArrayList<Noeud> previous = new ArrayList<Noeud>();
        previous.add(noeud);

        //parcours tant que l'on repasse pas par le même point
        do {
            Noeud previous_tmp = noeud;
            //parcours de tous les arcs du noeud courtant
            for (Arc arc : noeud.getNext()) {
                //si le noeud de la ligne n'a pas été visité
                if (arc.getLigne().equals(ligne) && !previous.contains(arc.getDestination())) {
                    noeud = arc.getDestination();
                    break;
                }
            }
            previous.add(previous_tmp);
        } while (!previous.contains(noeud));

        return noeud;
    }


    /**
     * Affiche les correspondances d'une station
     * @param station choisie
     */
    public void afficheCorrespondance(String station) {
        //parcours de tous les arcs du graph
        for(Map.Entry<String, ArrayList<Arc>> arcs : this.getArcs().entrySet()) {
            for (Arc arc : arcs.getValue()) {
                //si le nom de la station source ou destination est égale à station
                if (arc.getSource().getName().equals(station) || arc.getDestination().getName().equals(station)) {
                    System.out.println(arc.getLigne() + " ");
                }
            }
        }
    }

    /**
     * Méthode permettant le lancement de l'algorithme de plus court chemin
     */
    public void affichePlusCourtChemin() {
        //parcours de tous les arcs afin de les déselectionnés pour le graphique
        for(Map.Entry<String, ArrayList<Arc>> arcs : this.getArcs().entrySet()) {
            for (Arc arc : arcs.getValue()) {
                arc.getArcDrawable().setSelected(false);
            }
        }

        //execution de l'algorithme
        this.getCurrentAlgorithme().execute(this, this.getSource(), this.getDestination());

        //repaint du canvas
        Application.getWindowS().getjCanvas().repaint();
    }

    public AlgorithmePlusCourtChemin getCurrentAlgorithme() {
        return currentAlgorithme;
    }

    public void setCurrentAlgorithme(AlgorithmePlusCourtChemin currentAlgorithme) {
        this.currentAlgorithme = currentAlgorithme;

        System.out.println("set algo: " + currentAlgorithme.getClass());
    }

    public HashMap<String, ArrayList<Arc>> getArcs() {
        return arcs;
    }

    public void addArc(Arc arc) {
        String ligne = arc.getLigne();

        if (!this.getArcs().containsKey(ligne)) {
            this.getArcs().put(ligne, new ArrayList<Arc>());
        }

        ArrayList<Arc> arcLinkedList = this.getArcs().get(ligne);
        arcLinkedList.add(arc);
    }

    public void addNoeud(Noeud noeud) {
        this.getNoeuds().put(noeud.getId(),noeud);
    }

    public HashMap<Integer, Noeud> getNoeuds() {
        return noeuds;
    }

    public Noeud getSource() {
        return source;
    }

    public void setSource(Noeud source) {
        if(this.getSource() != null)
            this.getSource().getNoeudDrawable().setSelected(false);
        this.setDestination(this.getSource());
        this.source = source;
        this.getSource().getNoeudDrawable().setSelected(true);

        Application.getWindowS().getjButtonSource().setText("Source: " + this.getSource().getName());

        if (this.getSource() != null && this.getDestination() != null) {
            this.affichePlusCourtChemin();
        }
    }

    public Noeud getDestination() {
        return destination;
    }

    public void setDestination(Noeud destination) {
        if(this.getDestination() != null)
            this.getDestination().getNoeudDrawable().setSelected(false);
        this.destination = destination;
        if(this.getDestination() != null) {
            this.getDestination().getNoeudDrawable().setSelected(true);
            Application.getWindowS().getjButtonDestination().setText("Destination: " + this.getDestination().getName());
        }
    }

    @Override
    public String toString() {
        return "Graph{" +
                "arcs=" + arcs +
                ", noeuds=" + noeuds +
                ", source=" + source +
                ", destination=" + destination +
                ", currentAlgorithme=" + currentAlgorithme +
                '}';
    }
}
